/**
 * @file 	 meter_pumps.c
 * @brief
 * @author 	 wenbo
 * @version  1.0
 * @date 	 2025-01-16
 *
 * @copyright Copyright (c) 2024  WENBO
 *
 */
#include "meter_pumps.h"

#define LOGI println
#define LOGE println
#define LOGD(...) ;

#define CHECK_ERR(a, str, goto_tag, ...)                                                \
    do                                                                                  \
    {                                                                                   \
        if (!(a))                                                                       \
        {                                                                               \
            LOGE("%s(%d): " str, ##__VA_ARGS__);         		                        \
            goto goto_tag;                                                              \
        }                                                                               \
    } while (0)

#define CHECK_NULL(a, str, goto_tag, ...)   CHECK_ERR(a, str, goto_tag, ##__VA_ARGS__)
#define CHECK_NZERO(a, str, goto_tag, ...)  CHECK_ERR(0U == (a), str, goto_tag, ##__VA_ARGS__)


typedef struct {
    uint8_t 	slave_id;
    uint8_t 	port;
    uint32_t 	speed;
    uint8_t 	rec_data[64];
    uint8_t 	rec_len;
}Type_Com_Def;

struct Type_MeterPumps_Def
{
    Type_Com_Def 	com;
    bool            online;
    uint16_t        freq;
    uint32_t        interval_ms;
    uint64_t        timestamp;
    meter_pumps_event_cb_t  event_cb;
    void* user_data;
};

// modbus 失败重试次数
#define MODBUS_TRY_TIMES        3
// 默认轮询读取间隔
#define DEFAULT_INTERVAL_MS     2000

// 电磁计量泵设备寄存器定义
/*****************************************/
// 保持寄存器
#define ADDR_UART_PARAMS        0x4000
#define ADDR_SLAVE_ID           0x4001
#define ADDR_RUN_FREQ           0x4002
#define ADDR_START_STOP         0x4003
/*****************************************/


static bool isLoopThreadCreat = false;
static aos_mutex_t gMutex;
static Type_MeterPumps_CTX MeterPumpsList[10] = { 0 };

static void meter_pumps_process_thread(void* arg);

static void memcpy_resv_halfword(void* des, void* const res, uint16_t size)
{
    for (uint16_t i = 0; i < size; i += sizeof(uint16_t)) {
        *((uint8_t*)des + i + 0) = *((uint8_t*)res + i + 1);
        *((uint8_t*)des + i + 1) = *((uint8_t*)res + i + 0);
    }
}

static int xp_meter_pumps_write_reginsters(struct Type_MeterPumps_Def* const ctx, uint16_t addr, uint16_t* data, uint16_t reg_size)
{
    MB_data modbus;
    uint8_t try_times = MODBUS_TRY_TIMES;
    LOGD("xp_meter_pumps_write_reginsters addr: %04X, len: %d", addr, reg_size);

    modbus.slave_id = ctx->com.slave_id;
    modbus.mb_fun = (reg_size == 1) ? mb_single_w : mb_hold_w;
    modbus.start_addr = addr;
    modbus.wBuf = data;
    modbus.rwLen = reg_size;
    modbus.resBuf = ctx->com.rec_data;
    modbus.resLen = &ctx->com.rec_len;
    while (try_times--) {
        if (0 == xp_modbus_ask(ctx->com.port, &modbus)) return 0;
    }

    return -1;
}

static int xp_meter_pumps_read_reginsters(struct Type_MeterPumps_Def* const ctx, uint16_t addr, uint16_t* data, uint16_t reg_size)
{
    uint8_t resLen;
    MB_data modbus;
    uint8_t try_times = MODBUS_TRY_TIMES;
    LOGD("xp_meter_pumps_read_reginsters addr: %04X, reg_size: %d", addr, reg_size);

    modbus.slave_id = ctx->com.slave_id;
    modbus.mb_fun = mb_hold_r;
    modbus.start_addr = addr;
    modbus.rwLen = reg_size;
    modbus.resBuf = ctx->com.rec_data;
    modbus.resLen = &ctx->com.rec_len;
    while (try_times--) {
        if (0 == xp_modbus_ask(ctx->com.port, &modbus)) {
            memcpy_resv_halfword(data, ctx->com.rec_data, reg_size * sizeof(uint16_t));
            return 0;
        }
    }
    return -1;
}

/**
 * @brief       创建电磁计量泵 handle
 * @param[in]	port                RS485 端口
 * @param[in]	baudrate            波特率
 * @param[in]	slave_id            从机 ID
 * @param[in]	cb                  事件回调(非 NULL 则驱动器创建处理任务)
 * @return      Type_MeterPumps_CTX        handle
 */
Type_MeterPumps_CTX xp_meter_pumps_creat(uint8_t port, uint32_t baudrate, uint8_t slave_id, meter_pumps_event_cb_t cb, void* user_data)
{
    int ret = 0;
    Type_MeterPumps_CTX ctx = aos_malloc(sizeof(struct Type_MeterPumps_Def));
    CHECK_NULL(ctx, "meter_pumps ctx malloc failed", err_1);

    CHECK_NZERO(xp_modbus_init(port, baudrate, MB_PAR_NONE, 100), "meter_pumps modbus init failed", err_2);

    ctx->com.port = port;
    ctx->com.slave_id = slave_id;
    ctx->com.speed = baudrate;

    if (cb) {
        if (!isLoopThreadCreat) {
            aos_mutex_new(&gMutex);
            CHECK_NZERO(aos_task_new("meter_pumps_process_thread", meter_pumps_process_thread, NULL, 2048), "creat meter_pumps thread failed", err_2);
            isLoopThreadCreat = true;
        }

        aos_mutex_lock(&gMutex, AOS_WAIT_FOREVER);
        for (size_t i = 0; i < sizeof(MeterPumpsList) / sizeof(MeterPumpsList[0]); i++)
        {
            if (!MeterPumpsList[i]) {
                MeterPumpsList[i] = ctx;
                ctx->interval_ms = DEFAULT_INTERVAL_MS;
                ctx->online = false;
                break;
            }
        }
        aos_mutex_unlock(&gMutex);
    }
    LOGD("Creat MeterPumps port: %d, baudrate: %d, slave_id: %d success, ctx: %p", port, baudrate, slave_id, ctx);
    ctx->event_cb = cb;
    ctx->user_data = user_data;
    return ctx;

err_2:
    aos_free(ctx);
err_1:
    return NULL;
}
/**
 * @brief       销毁电磁计量泵 handle
 * @param[in]	ctx
 */
void xp_meter_pumps_del(Type_MeterPumps_CTX ctx)
{
    if (ctx) {
        for (size_t i = 0; i < sizeof(MeterPumpsList) / sizeof(MeterPumpsList[0]); i++)
        {
            if (ctx == MeterPumpsList[i]) {
                MeterPumpsList[i] = NULL;
            }
        }
        aos_free(ctx);
        *(&ctx) = NULL;
    }
}
/**
 * @brief       设置电磁计量泵运行频率
 * @param[in]	ctx                 handle
 * @param[in]	freq                运行频率(0~120)
 * @return      bool                true: 执行成功
 */
bool xp_meter_pumps_set_freq(Type_MeterPumps_CTX ctx, uint16_t freq)
{
    if (ctx) {
        return 0 == xp_meter_pumps_write_reginsters(ctx, ADDR_RUN_FREQ, &freq, 1);
    }
    return false;
}
/**
 * @brief       获取电磁计量泵运行频率
 * @param[in]	ctx                 handle
 * @param[out]	freq                运行频率结果
 * @return      bool                true: 执行成功
 */
bool  xp_meter_pumps_get_freq(Type_MeterPumps_CTX ctx, uint16_t* freq)
{
    if (ctx && freq) {
        return 0 == xp_meter_pumps_read_reginsters(ctx, ADDR_RUN_FREQ, freq, 1);
    }
    return false;
}
/**
 * @brief       通信方式控制电磁计量泵运行使能
 * @param[in]	ctx                 handle
 * @param[in]	enable              运行/停止
 * @return      bool                true: 执行成功
 */
bool xp_meter_pumps_enable_by_modbus(Type_MeterPumps_CTX ctx, bool enable)
{
    if (ctx) {
        uint16_t state = enable ? 1 : 0;
        return 0 == xp_meter_pumps_write_reginsters(ctx, ADDR_START_STOP, &state, 1);
    }
    return false;
}
/**
 * @brief       获取电磁计量泵运行频率
 * @param[in]	ctx                 handle
 * @param[out]	state               运行状态结果
 * @return      bool                true: 执行成功
 */
bool xp_meter_pumps_get_state(Type_MeterPumps_CTX ctx, bool* state)
{
    if (ctx && state) {
        uint16_t u16State;
        bool isok = 0 == xp_meter_pumps_read_reginsters(ctx, ADDR_START_STOP, u16State, 1);
        *state = u16State ? true : false;
        return isok;
    }
    return false;
}
/**
 * @brief       设置任务轮询读取间隔
 * @param[in]	ctx                 handle
 * @param[in]	interval_ms         轮询间隔
 * @return      bool                true: 执行成功
 */
bool xp_meter_pumps_set_loop_interval(Type_MeterPumps_CTX ctx, uint32_t interval_ms)
{
    if (ctx && isLoopThreadCreat) {
        ctx->interval_ms = interval_ms;
        return true;
    }
    return false;
}

static void meter_pumps_process_thread(void* arg)
{
    while (1)
    {
        uint64_t curr_time = aos_now_ms();
        aos_mutex_lock(&gMutex, AOS_WAIT_FOREVER);

        for (size_t i = 0; i < sizeof(MeterPumpsList) / sizeof(MeterPumpsList[0]); i++)
        {
            if (MeterPumpsList[i]) {
                if (curr_time - MeterPumpsList[i]->timestamp > MeterPumpsList[i]->interval_ms) {
                    MeterPumpsList[i]->timestamp = curr_time;
                    uint16_t freq;

                    bool isok = xp_meter_pumps_get_freq(MeterPumpsList[i], &freq);
                    if (isok) {
                        if (!MeterPumpsList[i]->online) {
                            MeterPumpsList[i]->online = true;
                            MeterPumpsList[i]->event_cb(EVT_ONLINE, NULL, MeterPumpsList[i]->user_data);
                        }
                        if (freq != MeterPumpsList[i]->freq) {
                            MeterPumpsList[i]->freq = freq;
                            MeterPumpsList[i]->event_cb(EVT_FREQ_CHANGE, &freq, MeterPumpsList[i]->user_data);
                        }
                    }
                    else {
                        if (MeterPumpsList[i]->online) {
                            MeterPumpsList[i]->online = false;
                            MeterPumpsList[i]->event_cb(EVT_OFFLINE, NULL, MeterPumpsList[i]->user_data);
                        }
                    }
                }
            }
        }

        aos_mutex_unlock(&gMutex);
        aos_msleep(100);
    }
}